// 在vscode里中文输入法不显示选字框：editor.editContext关掉

#include<iostream>
using namespace std;

/****************** 1、函数重载 ******************/

// C++支持同一作用域有同名函数，这些函数的形参的参数类型 或 个数 或 类型顺序不同

#if 0
// 1.参数类型不同
// 形参为int、int
void Swap(int* pa, int* pb)
{
    cout << "void Swap(int* pa, int* pb)" << endl;
    int t = *pa; 
    *pa = *pb;
    *pb = t;
}
// 形参为double、double
void Swap(double* pa, double* pb)
{
    cout << "void Swap(double* pa, double* pb)" << endl;
    double t = *pa; 
    *pa = *pb;
    *pb = t;
}

int main()
{
    int a = 1, b = 2;
    Swap(&a, &b);
    
    double c = 2.2, d = 3.3;
    Swap(&c, &d);
    return 0;
}

// 2.参数个数不同
void func()
{
    cout << "void func()" << endl;
}
void func(int a)
{
    cout << "void func(int a)" << endl;
}
int main()
{
    func(1);
    func();
    return 0;
}

// 3.参数顺序不同
void func(int a, double b)
{
    cout << "void func(int a, double b)" << endl;
}
void func(double b, int a)
{
    cout << "void func(double b, int a)" << endl;
}

int main()
{
    func(1, 1.4);
    func(2.2, 1);
    return 0;
}

// 注：返回值不同不构成重载
// 编译错误
void func()
{
    cout << "void func()" << endl;
}
int func()
{
    cout << "int func()" << endl;
    return 0;
}

int main()
{
    func();
    func();
    return 0;
}

// 小练习：
void func()
{
    cout << "void func()" << endl;
}
int func(int a = 10)
{
    cout << "int func(int a = 10)" << endl;
    return 0;
}

int main()
{
    func(2);    // 正常运行
    func();     // 调用歧义
    return 0;
}
// 上面两个函数构成重载（参数个数不同），但会有调用歧义
#endif

/*
C   是用函数名去找函数地址
C++ 是用修饰后的函数名去找函数地址 (根据形参修饰函数名)，不同的编译器修饰规则可能不同

// VS2022 可测试如何修饰
int f(int a, char b);
int f(char b, int a);

int main()
{
    f(1, 'a');
    f('z', 2);
    return 0;
}
*/

/****************** 2、引用  ******************/

#if 0
int main()
{
    int a = 0;
    int& b = a; // b是a的别名

    printf("a地址：%p\n", &a);
    printf("b地址：%p\n", &b);
    // a与b都是同一块空间

    int& c = b; // 又给b取别名
    printf("c地址：%p\n", &c);

    b++;
    printf("a: %d, b: %d, c: %d\n", a, b, c);

    a++;
    printf("a: %d, b: %d, c: %d\n", a, b, c);

    c++;
    printf("a: %d, b: %d, c: %d\n", a, b, c);
    return 0;
}

// 用法1 -- 函数传参

void Swap(int& a, int& b)
{
    int t = a;
    a = b;
    b = t;
}

int main()
{
    int x = 1, y = 2;
    Swap(x, y); // 传参时，a是x的别名，本质都是同一块空间，修改a就会修改x。变量y也是同理
    printf("x: %d, y: %d\n", x, y);

    { // 测试
    int* px = &x;
    int*& pr = px;
    pr = nullptr;
    }

    // 注：定义时必须初始化
    // int& a; // err

    // 代码阅读
    int a = 1, b = 0;
    int& x = a;
    x = b; // 此行代码并不是给b取个别名x，x跟a是同一块空间，此处是将b赋值给x
    return 0;
}



//// 权限问题
// 常引用
// 引用跟指针有权限的概念

int main()
{
    // 回顾指针：
    // // 1.指针权限放大 err
    // int m;
    // const int* p1 = &m; // const在*之前，可以修改指针指向，不可修改其指向的内容
    // int* p2 = p1;   // 也存在权限放大，p1指向的内容不可修改

    // // 2.指针权限缩小
    // int n;
    // int* pn = &n;
    // const int* pn1 = pn;

    // 引用：
    const int a = 10;
    // int& x = a; // err  1.权限放大。a是只读的，x可读可写
    {
        const int a = 10;
        int p = a; // 这里是将a赋值给p，不涉及权限放大，不要与上面混淆
    }

    const int& x = a; // 这样才行，x与a的权限是相同的。2.权限平移

    int b = 10;
    const int& y = b; // b可读可写，y只读。3.权限缩小
    b++;
    cout << y << endl; // 可以通过b修改y

    
    return 0;
}

// 总结：权限可以平移/缩小，不可放大



int main()
{
    // // 1.类型转换
    // double d = 3.14;
    // int i = d; // double类型赋值给int类型，会舍弃小数部分，将整数部分存放在临时变量中，在将临时变量赋值给变量i

    // // 类型转换会产生临时变量，临时变量是只读的，不可写
    // // int& j = d; // err 权限放大
    // const int& k = d;   

    // // 2.表达式计算
    // int a = 0, b = 3;
    // int c = a + b;  // 表达式计算也会产生临时变量, 本质上是将a + b的结果保存在临时变量中，再赋值给c
    // const int& x = a + b; // 需要加const

    // // 3.底层 本质跟指针相同（了解即可）
    int* p = nullptr;
    int& r = *p;

    // cout << r; // 这里才会异常
    return 0;
}

// 用法2 -- 做返回值
// 。。。



/*************** 改进C语言的宏函数 -- inline ***************/
// 对于一些频繁调用的小函数，加上inline可在一定程度上提高效率
// 用法举例：
inline void Swap(int& a, int& b)
{
    int t = a; a = b; b = t;
}
// 在编译阶段会用函数体替换函数调用，会使生成的可执行程序变大
// 注：加上inline只是对编译器提个建议，而且不建议声明跟定义分离（函数被展开了就没有函数地址，会发生链接错误）

inline void func();
/* func.cpp:
inline void func()
{
    cout << "H";
}
*/
int main()
{
    func();
    auto& F = func;
    F();
    return 0;
}


/**************** auto 关键字，类型自动推导, 简化代码 ****************/
int main()
{
    auto a = 0; // 等价于 int a = 0; 下面两行也是类似
    auto b = 3.14;
    auto c = 'a';

    // typeid 查看变量类型 (不同编译器可能输出不同)
    cout << typeid(a).name() << endl; // i - int
    cout << typeid(b).name() << endl; // d - double
    cout << typeid(c).name() << endl; // c - char


    // 指定p必须是指针
    auto* p = &a;   // 等价于 int* p = &a;

    auto& x = a;    // 等价于 int& x = a;

{ // 小补充
    typedef int* pint;
    // const pint p1;   //err const修饰的变量必须要初始化
    const pint* p2;     // 这里不会
}

    return 0;
}

// auto 不能用的场景    
void func(auto a)
{}

int main()
{
    // 1.
    // auto a = 3, b = 'x'; // a与b类型不同

    // 2.不能声明数组
    // auto a[5] = { 1,2,3,4,5 };

    // 3.不能做函数的参数
    return 0;
}

#endif

// C++11 范围for 

int main()
{
    int arr[5] = { 1,2,3,4,5 };

    // for(auto e : arr) // 从头到尾遍历数组
    //     cout << e << ' ';

    // 每个元素都加上2
    for(auto& e : arr)
        e += 2;
    for(int i = 0; i < 5; i++)
        cout << arr[i] << ' ';
    return 0;
}
